
package todolist.entity;

import java.util.LinkedList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.ListIterator;

/**
 * Abstraction of a collection of Tasks.
 * Tasks not guaranteed to be sorted.
 * Cloning creates another container with a shallow copy of Tasks.
 * @see todolist.entity.TaskEntity
 * @author Ivan
 */
public class TaskContainerEntity implements Cloneable {
	
	private LinkedList<TaskEntity> _lstTaskEntities;
	private long _lngLastTaskId; // Task ID cannot be 0 or negative, so previous ID will be 0

	// constructors
	
	/**
	 * Create new empty collection of Tasks with ID no less than 0.
	 */
	public TaskContainerEntity() {
		this(0L);
	}
	
	/**
	 * Create new empty collection of Tasks with specified non-negative minimum ID.
	 * @throws IllegalArgumentExcpetion
	 */
	public TaskContainerEntity(long plngMinimumID) throws IllegalArgumentException {
		if (plngMinimumID < 0)
			throw new IllegalArgumentException("TaskContainerEntity.TaskContainerEntity(): Minimum ID cannot be < 0");
		_lstTaskEntities = new LinkedList<TaskEntity>();
		_lngLastTaskId = plngMinimumID;
	}

	/**
	 * Create new collection of Tasks from another TaskContainer.
	 * Time complexity: O(n) currently
	 * The collection is a shallow copy i.e. this TaskContainer holds the same task as the other TaskContainer.
	 * Tasks retain their order as accessed by the other TaskContainer's iterator.
	 */
	public TaskContainerEntity(TaskContainerEntity pentOther) {
		this(pentOther._lngLastTaskId);
		_lstTaskEntities.addAll(pentOther._lstTaskEntities); // create shallow copy from other to this
	}
	
	// getters
	
	/**
	 * Gets number of Tasks in TaskContainer.
	 */
	public int getNumberOfTasks() {
		return _lstTaskEntities.size();
	}
	
	/**
	 * Gets an ListIterator over the Tasks in this TaskContainer, adjusted by a sort() or any items add()-ed.
	 * This ListIterator is for traversing Tasks only, attempts to use the ListIterator's remove(), set(), or add() will result in OperationUnsupportedException being thrown.
	 */
	public ListIterator<TaskEntity> getListIterator() {
		return _lstTaskEntities.listIterator();
	}
	
	/**
	 * Gets the last added, and hence largest, Task ID.
	 * Returns typically 0, if there are no tasks yet added.
	 */
	public long getLastTaskId() {
		return _lngLastTaskId;
	}
	
	// facilitators

	/**
	 * Adds a Task to the first position in this TaskContainer.
	 * Time complexity: O(1) currently
	 * Task must not be null and Task ID has to be larger than the Task ID last added.
	 * @throws IllegalArgumentException
	 */
	public void addTask(TaskEntity pentTask) throws IllegalArgumentException {
		if (pentTask == null)
			throw new IllegalArgumentException("TaskContainerEntity.addTask(): Task must not be null.");
		if (pentTask.getTaskId() <= _lngLastTaskId)
			throw new IllegalArgumentException("Task ID must be greater than " + String.valueOf(_lngLastTaskId));
		_lstTaskEntities.addFirst(pentTask);
		_lngLastTaskId = pentTask.getTaskId();
	}
	
	/**
	 * Deletes Task residing in this TaskContainer by specified Task ID.
	 * Time complexity: O(n) currently
	 * Returns whether Task with specified Task ID was indeed found in this TaskContainer.
	 * Task ID of negative or 0 will result in IllegalArgumentException being thrown.
	 */
	public boolean deleteTask(long plngTaskId) throws IllegalArgumentException {
		if (plngTaskId <= 0)
			throw new IllegalArgumentException("TaskContainerEntity.deleteTask(): Task ID must not be negative or 0.");
		return _lstTaskEntities.remove(new TaskEntity(plngTaskId, "dummy"));
	}
	
	/**
	 * Deletes Task residing in this TaskContainer.
	 * Time complexity: O(n) currently
	 * Returns whether provided Task with was indeed found in this TaskContainer.
	 * null parameter will result in IllegalArgumentException being thrown.
	 * @throws IllegalArgumentException
	 */
	public boolean deleteTask(TaskEntity pentTask) throws IllegalArgumentException {
		if (pentTask == null)
			throw new IllegalArgumentException("TaskContainerEntity.deleteTask(): Task must not be null.");
		return _lstTaskEntities.remove(pentTask);
	}

	/**
	 * Finds Task residing in this TaskContainer by specified Task ID.
	 * Time complexity: O(n) currently
	 * null if not found.
	 */
	public TaskEntity findTask(long plngTaskId) {
		ListIterator<TaskEntity> objItr = _lstTaskEntities.listIterator();
		while (objItr.hasNext()) {
			TaskEntity entTask = objItr.next();
			if (entTask.getTaskId() == plngTaskId) // if match by Task ID found
				return entTask;
		}
		return null; // no match
	}
	
	/**
	 * Orders Tasks in collection so that subsequent ListIterators will reflect this order.
	 * Comparator provided must not be null.
	 * Time complexity: O(n log n)
	 * @throws IllegalArgumentException
	 */
	public void sortTasks(Comparator<TaskEntity> pobjComparator) throws IllegalArgumentException {
		if (pobjComparator == null)
			throw new IllegalArgumentException("TaskContainerEntity.sortTasks(): A Comparator<TaskEntity> is required.");
		// store Tasks in temp array, sort array
		TaskEntity[] arrTempTaskEntities = new TaskEntity[_lstTaskEntities.size()];
		arrTempTaskEntities = _lstTaskEntities.<TaskEntity>toArray(arrTempTaskEntities); // O(n)
		Arrays.<TaskEntity>sort(arrTempTaskEntities, pobjComparator); // + O(n log n)
		// copy Tasks back into Collection in order
		_lstTaskEntities.clear();
		for (TaskEntity entTask : arrTempTaskEntities) // + O(n)
			_lstTaskEntities.addLast(entTask); // adds to end of list
	}
	
	// overrides

	/**
	 * Create new collection of Tasks, a shallow copy of tasks in this TaskContainer.
	 * Time complexity: O(n)
	 * Tasks retain their order as accessed by the other TaskContainer's iterator.
	 */
	@Override
	public Object clone() {
		return new TaskContainerEntity(this);
	}
	
}
